package xj.model.uml;

import static xj.model.uml.UMLModelConstants.*;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class Attribute extends Feature {

  public Attribute() {
    this(null, null);
  }

  public Attribute(String name) {
    this(name, null);
  }

  public Attribute(String name, String type) {
    super(name, type);
    destructable = false;
    preserved = true;
  }

  public Attribute(String name, String type, Visibility visibility) {
    super(name, type, visibility);
    destructable = false;
    preserved = true;
  }

  @Override
  public Object clone() throws CloneNotSupportedException {
    Attribute attr = (Attribute) super.clone();
    attr.setInitValue(initValue);
    attr.setChangeability(changeability);
    attr.setMultiplicity(multiplicity);
    attr.setOrdered(ordered);
    attr.setUnique(unique);
    attr.setConstant(constant);
    attr.setReadOnly(readOnly);
    return attr;
  }

  public static Attribute makeAttribute(String astring) {
    return Attribute.makeAttribute(null, astring);
  }

  public static Attribute makeAttribute(Attribute attribute, String astring) {
    if (astring != null) {
      if (attribute == null) {
	attribute = new Attribute();
      }
      String pstring = null;

      astring = astring.trim();
      int len = astring.length();
      char c = astring.charAt(len - 1);
      if (c == '}') {
	int k = astring.lastIndexOf("{");
	if (k >= 0) {
	  pstring = astring.substring(k + 1, len - 1);
	  astring = astring.substring(0, k).trim();
	}
      }

      int i = 0;
      if (astring.length() > 0) {
	c = astring.charAt(0);
	if (c == '\u00AB') {
	  i = astring.indexOf('\u00BB');
	  if (i > 1) {
	    String st = astring.substring(1, i);
	    attribute.setStereotypes(st);
	  } else {
	    i = 0;
	  }
	  astring = astring.substring(i + 1).trim();
	  c = astring.charAt(0);
	  i = 0;
	}

	Visibility v = Visibility.Default;
	switch (c) {
	case '+':
	  v = Visibility.Public;
	  i = 1;
	  break;

	case '-':
	  v = Visibility.Private;
	  i = 1;
	  break;

	case '#':
	  v = Visibility.Protected;
	  i = 1;
	  break;

	case '~':
	  v = Visibility.Package;
	  i = 1;
	  break;

	default:
	}
	attribute.setVisibility(v);
	if (i > 0) {
	  astring = astring.substring(i).trim();
	}

	i = astring.indexOf('/');
	if (i >= 0) {
	  attribute.setChangeability(Changeability.Derived);
	  astring = astring.substring(i + 1).trim();
	}

	i = astring.indexOf(':');
	if (i < 0) {
	  attribute.setName(astring);
	} else {
	  attribute.setName(astring.substring(0, i).trim());
	  astring = astring.substring(i + 1).trim();

	  i = astring.indexOf('[');
	  if (i >= 0) {
	    attribute.setType(astring.substring(0, i).trim());
	    int j = astring.indexOf(']', i);
	    if (j > i) {
	      attribute.setMultiplicity(Multiplicity.toMultiplicity(astring
		  .substring(i + 1, j).trim()));
	    } else {
	      j = i;
	    }
	    i = astring.indexOf('=', j);
	    if (i > j) {
	      attribute.setInitValue(astring.substring(i + 1).trim());
	    }
	  } else {
	    i = astring.indexOf('=');
	    if (i < 0) {
	      attribute.setType(astring);
	    } else {
	      attribute.setType(astring.substring(0, i).trim());
	      attribute.setInitValue(astring.substring(i + 1).trim());
	    }
	  }
	}
      }

      if (pstring != null && pstring.length() > 0) {
	for (Changeability ch : Changeability.values()) {
	  if (pstring.indexOf(ch.toString()) >= 0) {
	    attribute.setChangeability(ch);
	    break;
	  }
	}

	for (CollectionType ct : CollectionType.values()) {
	  if (pstring.indexOf(CollectionType.toString(ct)) >= 0) {
	    attribute.setCollectionType(ct);
	    break;
	  }
	}
      }
    }
    return attribute;
  }

  public String getInitValue() {
    return initValue;
  }

  public void setInitValue(String initValue) {
    this.initValue = initValue;
    if (initValue != null) {
      initValue = initValue.trim();
    }
    setAttribute(ATTR_INIT_VALUE, initValue);
  }

  public Changeability getChangeability() {
    return changeability;
  }

  public void setChangeability(Changeability changeability) {
    this.changeability = changeability;
    setAttribute(ATTR_CHANGEABILITY, changeability);
  }

  public Multiplicity getMultiplicity() {
    return multiplicity;
  }

  public void setMultiplicity(Multiplicity multiplicity) {
    this.multiplicity = multiplicity;
    setAttribute(ATTR_MULTIPLICITY, multiplicity);
  }

  public CollectionType getCollectionType() {
    if (multiplicity != null && multiplicity.isMoreThanOne()) {
      return CollectionType.toCollectionType(ordered, unique);
    } else {
      return null;
    }
  }

  public void setCollectionType(CollectionType c) {
    if (c != null) {
      setOrdered(CollectionType.isOrdered(c));
      setUnique(CollectionType.isUnique(c));
    }
  }

  public boolean isOrdered() {
    return ordered;
  }

  public void setOrdered(boolean n) {
    ordered = n;
    setAttribute(ATTR_ORDERED, n);
  }

  public boolean isUnique() {
    return unique;
  }

  public void setUnique(boolean n) {
    unique = n;
    setAttribute(ATTR_UNIQUE, n);
  }

  public boolean isConstant() {
    return constant;
  }

  public void setConstant(boolean n) {
    constant = n;
    setAttribute(ATTR_CONSTANT, n);
  }

  public boolean isReadOnly() {
    return readOnly;
  }

  public void setReadOnly(boolean n) {
    readOnly = n;
    setAttribute(ATTR_READONLY, n);
  }

  @Override
  public String toString() {
    return toString(false, false);
  }

  @Override
  public String toString(boolean withStereotype) {
    return toString(true, false);
  }

  public String toString(boolean withStereotype, boolean withProperties) {
    StringBuilder sb = new StringBuilder();
    if (withStereotype) {
      Set<String> st = getStereotypes();
      if (st != null && st.size() > 0) {
	sb.append("\u00AB").append(getStereotypeString()).append("\u00BB ");
      }
    }

    Visibility vis = getVisibility();
    if (vis != null && 
	vis != Visibility.Default) { 
      sb.append(vis.toChar()).append(' ');
    }
    if (changeability == Changeability.Derived) {
      sb.append("/ ");
    }
    sb.append(name);
    if (type != null) {
      sb.append(": " + type);
      if (multiplicity != null && multiplicity != Multiplicity.One) {
	sb.append("[").append(Multiplicity.toString(multiplicity)).append("]");
      }
    }
    if (initValue != null && initValue.length() > 0) {
      sb.append(" = " + initValue);
    }

    if (withProperties) {
      String prop = getPropertiesString();
      if (prop != null) {
	sb.append(" ").append(prop);
      }
    }
    return sb.toString();
  }

  public String getPropertiesString() {
    StringBuilder sb = new StringBuilder();
    Set<String> pset = new HashSet<String>();
    if (changeability != null && changeability != Changeability.Changeable
	&& changeability != Changeability.Derived) {
      pset.add(changeability.toString());
    }
    CollectionType c = getCollectionType();
    if (c != null) {
      pset.add(CollectionType.toString(c));
    }

    if (pset.size() > 0) {
      boolean first = true;
      Iterator<String> pi = pset.iterator();
      sb.append(" {");
      while (pi.hasNext()) {
	if (first) {
	  first = false;
	} else {
	  sb.append(", ");
	}
	sb.append(pi.next());
      }
      sb.append("}");
    }
    if (sb.length() > 0) {
      return sb.toString();
    } else {
      return null;
    }
  }

  protected String initValue = null;

  protected Multiplicity multiplicity = null;

  protected Changeability changeability = Changeability.Changeable;

  protected boolean ordered = false;

  protected boolean unique = true;

  protected boolean constant = false;

  protected boolean readOnly = false;  

}
